from randomname import getName as getName
#enum NodeType
from btdefinition import (NodeType, NodeState, BTNode)
from EventController import (EntityEvents)
import copy



'''
A simulation of a blackboard
'''
class MemoryPad:
    def __init__(self):
        self.mem1 = None
        self.mem2 = 1
        self.mem3 = 2

class EntityContext:
    def __init__(self, tree, entity, eventHandlerDict):
        self.stack = []
        self.state = NodeState.START
        self.memorypad = MemoryPad()
        self.commands = []
        self.tree = []
        for i in tree:
            self.tree.append(copy.copy(i))
        self.entity = entity
        self.eventHandler = {}
        if not eventHandlerDict == None:
            for i in eventHandlerDict:
                tree = []
                self.eventHandler[i] = [False, EntityContext(eventHandlerDict[i], self.entity, None)]
                self.entity.entityEventController.subscribe(i, self.onEventInterrupt, "ALL")
    
    def onEventInterrupt(self, eventType):
        inProcess = self.eventHandler[eventType][0]
        if not inProcess:
            self.eventHandler[eventType][0] = True
    
                
    
    def prepare(self):
        self.state = self.memorypad.mem1       
    def __str__(self):
        return self.entity.name
    def __repr__(self):
        return "entity:"+self.entity.name

class BTManager:
    def __init__(self, flattree, entities, entityManager, eventHandlerDict):
        self.entContext = []
        self.entityManager = entityManager;
        for i in range(len(entities)):
            self.entContext.append(EntityContext(flattree, entities[i], eventHandlerDict))
        self.ctx = None
            
    def _handleSequential(self, node, idx):
            if node.processChildIdx == 0:
                node.idx = idx
                idx += 1
                node.processChildIdx = idx #pick the next one to the root for seq.
                self.ctx.state = NodeState.START
                self.ctx.stack.append(node)
                node = self.ctx.tree[idx]
            else:
                if self.ctx.state == NodeState.FAIL:
                    node.processChildIdx = 0 #reset
                    idx = node.idx
                    node = self.ctx.stack.pop() #pop stack to return to parent of this 
                else:
                    if idx >= node.idx + node.nodeLength:
                        node.processChildIdx = 0 #we're beyond the range of this tree branch, reset.
                        self.ctx.stack.pop() #pop myself.
                        node = self.ctx.stack[-1] #return to parent
                    else:
                        #continue onto the next child to process.
                        node.processChildIdx = idx #set the next child to process to the current instruction counter.
                        node = self.ctx.tree[idx]
                        #self.ctx.stack.append(node)
            return node, idx
    def _handleAction(self, node, idx):
        node.idx = idx
        idx += 1
        if node.type == NodeType.INSTANT_CONDITION:
            print "instant condition"
            self.ctx.state == NodeState.SUCCESS
        elif node.type == NodeType.INSTANT_ACTION:
            print "instant action"
            self.ctx.state == NodeState.SUCCESS
        else:
            if (self.ctx.state == NodeState.START):     
                 self.ctx.state = NodeState.PROCESSING
                 self.ctx.commands.append((node.key, node._typeToStr()))           
            else:
                self.ctx.state = NodeState.START #reset for the next one. Because if the current state is success, then we process the next node. If fail we don't really care.
        node = self.ctx.stack[-1] #return to parent. Remember leaf nodes are never pushed onto the stack. The parent of a leaf node is always on top of stack.
        return node, idx
    """
    This method will handle a particular node. It will track internal state.
    It will update the instruction pointer idx.
    """
    def _handleNode(self, node, idx):
        if (node.type == NodeType.SEQUENTIAL) or (node.type == NodeType.RANDOM):
            return self._handleSequential(node, idx)  
        elif (node.type == NodeType.PRIORITY):
            return self._handlePriority(node, idx)
        elif (node.type == NodeType.CONDITION) or (node.type == NodeType.ACTION):
            return self._handleAction(node, idx)
        else:
            raise EOFError()
    def executeCommands(self):
        self.entityManager.executeCommands(self.ctx)
    
    def update(self):
        for i in self.entContext:
            interrupted = False
            for key in i.eventHandler:
                """
                WARNING! The current design all interrupts are EQUAL. Interrupts cannot be interrupted!
                Interrupts will be processed one after the next!
                """
                interrupt = i.eventHandler[key]
                if interrupt[0] is True: #we need to process this context
                    self._update(interrupt[1])
                    interrupted = True
                    if self.ctx.state == NodeState.QUIT:
                        interrupt[0] = False
                        interrupted = False #If during this update the interrupt has completed, we don't interrupt again.
            if not interrupted:
                self._update(i) #resume    
    def _update(self, entContext):
            self.ctx = entContext
            #print "Updating Entity: ", self.ctx
            node = None
            self.ctx.commands = []
            if len(self.ctx.stack) == 0: #start from root because nothing is on the stack
                node = self.ctx.tree[0]
                self.ctx.state = NodeState.START
                idx = 0
            else:
                self.ctx.prepare()
                node = self.ctx.stack[-1]
                idx = node.processChildIdx
                
            while (not self.ctx.state == NodeState.QUIT) and (not self.ctx.state == NodeState.PROCESSING):
                try:
                    node, idx = self._handleNode(node, idx)    
                except IndexError: 
                    self.ctx.state = NodeState.QUIT
            self.executeCommands()


        

            
            